package wiiusej.apptest.statistics.performance;

import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Vector;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;

import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;

import wiiusej.wiiusejevents.GenericEvent;

public class PerformanceController implements Runnable{

	//Logger 
	private static Logger fileLogger;
	private static Logger performanceResultLogger;
	private static Logger performanceChunkLogger;
	private static Logger motionLogger;
	
	//COM QUEUES 
	private BlockingQueue<Track> fromAnywhere;
	
	//Results printing
	private final int RESULTS_LENGTH_ACC = 200;
	private final int RESULTS_LENGTH_CH = 100;
	private boolean printedAcc = false;
	private boolean printedCh = false;
	
	private int chunkCounter = 1;
	//state machine
	private boolean listening = true;
	
	
	//performance data structures
	private HashMap<Long, AccelerationPerformanceUnit> accelerationPerformance = 
		new HashMap<Long, AccelerationPerformanceUnit>();
	
	private HashMap<Long, ChunkPerformanceUnit> chunkPerformance = 
		new HashMap<Long, ChunkPerformanceUnit>();
	
	
	
	public PerformanceController(BlockingQueue<Track> fromSystem){
		this.fromAnywhere = fromSystem;		
		
		//Logger init
		PropertyConfigurator.configure("log4j.properties");
		fileLogger = Logger.getLogger("performanceController");
		performanceResultLogger = Logger.getLogger("performanceAccelerationResult");
		performanceChunkLogger = Logger.getLogger("performanceChunkResult");
		motionLogger = Logger.getLogger("motionPerformance");
				
	}
	

	@Override
	public void run() {

		while(listening){			
			//listen the queue for incomming reports
			Track track = fromAnywhere.poll();
			
			if(track !=null){
				
				computeIncomingTracks(track);
				//addTrack(track);		
				
				/*
				if(accelerationPerformance.size() > RESULTS_LENGTH && 
						!printed){				
					printResults();		
					printed = true;
					fileLogger.info("MEASURING PROCESS FINISHED: " + RESULTS_LENGTH);
				}*/
			}
		}
	}
	
	private void computeIncomingTracks(Track track){
		
		
		switch(track.getTrackType()){
		
			case ACCELERATION:
				
				addAccelerationTrack(track);
				
				/*
				if(accelerationPerformance.size() > RESULTS_LENGTH_ACC && 
						!printedAcc){				
					//printResults(TrackType.ACCELERATION);		
					printedAcc = true;
					fileLogger.info("MEASURING PROCESS FINISHED: " + RESULTS_LENGTH_ACC);
				}*/
				
				
				break;
		
			case CHUNK:
				
				addChunkTrack(track);
				
				if(chunkPerformance.size() > RESULTS_LENGTH_CH && 
						!printedCh){				
					
					performanceResultLogger.info("Printing CHUNK results!!");
					printResults(TrackType.CHUNK);		
					printedAcc = true;
					
					fileLogger.info("MEASURING CHUNK PROCESS FINISHED: " + RESULTS_LENGTH_CH);
					listening = false;
					
				}
				
				
				break;
		}
		
	}

	private void addChunkTrack(Track track) {
		ChunkPerformanceUnit unit;
		
		performanceChunkLogger.info("");
		performanceChunkLogger.info("IncomingTrack: " + track.getPacketID() + " From: "
				+ track.getClassName() + " Type: " + track.getTrackType() + 
				" Time: " + track.getTimeIN());
		
		if(chunkPerformance.get(track.getPacketID())!=null){
			unit = chunkPerformance.get(track.getPacketID());			
			if(!unit.addTrack(track)){
				fileLogger.debug("PerformanceUnit COMPLETED - TRACK DISCARTED!!");
			}	
			
			if(unit.isCompleted()){
				fileLogger.info("Chunk: " + chunkCounter);
				chunkCounter++;
			}
						
		}else{			
			unit = new ChunkPerformanceUnit(track.getWiimoteID(), track.getPacketID());	
			unit.addTrack(track);
			chunkPerformance.put(unit.getPacketID(), unit);
		}
		
	}

	private void addAccelerationTrack(Track track) {
		PerformanceUnit unit;
				
		//fileLogger.info("");
		fileLogger.debug("Incoming Track: " + track.getPacketID() + 
				" From: " + track.getClassName());
		
		if(accelerationPerformance.get(track.getPacketID())!=null){
			unit = accelerationPerformance.get(track.getPacketID());
			fileLogger.debug("Performance Unit: " + unit.getPacketID() + " EXISTS!!");
			fileLogger.debug("Performance Unit: " + unit.getPacketID() + " Adding Track From: " + 
					track.getClassName());

			if(!unit.addTrack(track)){
				fileLogger.debug("PerformanceUnit COMPLETED - TRACK DISCARTED!!");
			}			
			
		}else{			
			unit = new AccelerationPerformanceUnit(track.getWiimoteID(), track.getPacketID());
			fileLogger.debug("NEW Performance Unit: ");
					
			fileLogger.debug("Performance Unit: " + unit.getPacketID() + " Adding Track From: " + 
					track.getClassName());
			
			unit.addTrack(track);
			accelerationPerformance.put(unit.getPacketID(), (AccelerationPerformanceUnit) unit);			
		}
		
	}
		
	private void printResults(TrackType trackType){
		SimpleDateFormat tsFormat = new SimpleDateFormat("mm:ss:SSS");
		//Iterator it = accelerationPerformance.entrySet().iterator();
		Vector<PerformanceUnit> completed = new Vector<PerformanceUnit>();
		
		Iterator it;
		if(trackType.equals(TrackType.ACCELERATION)){			
			it = accelerationPerformance.entrySet().iterator();			
		}else{
			it = chunkPerformance.entrySet().iterator();
		}
		
		
		performanceResultLogger.info("****************** PERFORMANCE RESULTS ******************");
		//fileLogger.info("");
		//fileLogger.info("");
		
		while(it.hasNext()){
			Map.Entry entry = (Map.Entry) it.next();
			PerformanceUnit unit = (PerformanceUnit) entry.getValue();
						
			if(unit.isCompleted()){
				completed.add(unit);
				
				performanceResultLogger.info("PerformanceUnit: " + unit.getPacketID());
				
				for(Track track : unit.getTracks()){
					performanceResultLogger.info("Track: " + track.getClassName() + 
							" timeIN: " + tsFormat.format(track.getTimeIN()));
					
				}
				
				//clean hashmap
				//performanceUnits.remove(unit);
				performanceResultLogger.info("");				
			}			
		}
		
		performanceResultLogger.info("");
		performanceResultLogger.info("");
		performanceResultLogger.info("");
		
		printDelay(completed);
		
	}
	
	
	private void printDelay(Vector<PerformanceUnit> completed) {
		for(PerformanceUnit unit : completed){
			performanceResultLogger.info(unit.getTotalDelay());			
		}		
	}

	
	
	
	/*
	private void printResults(){
		SimpleDateFormat tsFormat = new SimpleDateFormat("mm:ss:SSS");
		Iterator it = performanceUnits.entrySet().iterator();
		
		fileLogger.info("****************** PERFORMANCE RESULTS ******************");
		fileLogger.info("");
		
		while(it.hasNext()){
			Map.Entry entry = (Map.Entry) it.next();
			Vector<PerformanceUnit> reports = (Vector<PerformanceUnit>) entry.getValue();
			
			//long totalTime = 0;
			fileLogger.info("ReportsOfPacket: " + reports.lastElement().getPacketID() + 
					" #: " + reports.size());
			
			long totalTime = 0;
			
			
			PerformanceUnit last = null, current=null;
			
			for(PerformanceUnit unit : reports){
				//totalTime += unit.getTimeStamp().getTime();
				fileLogger.info("TimeStamp: " + tsFormat.format(unit.getTimeStamp()));
				current = unit;
								
				if(last != null){
					long partialTime = current.getTimeStamp().getTime() - last.getTimeStamp().getTime();
					totalTime += partialTime;
					
					fileLogger.info("DelayTime[" +  reports.indexOf(last) + "/" + 
							reports.indexOf(current) + "]: " +  partialTime + 
							" AccumulatedTime: " + totalTime);
					
					
				}				
				last = unit;
			}
			
			
			fileLogger.info("");
			
			//fileLogger.info("TotalTime[MILLI]: " + totalTime + 
					//" TotalTime: " + tsFormat.format(totalTime));			
			
		}
		
		fileLogger.info("");
		
		
	}*/

	
	
	
	
}
